#ifndef __TD_XVERTEX__
#define __TD_XVERTEX__

#include "Line.hpp"
#include "base/Graphic.hpp"

#include <cstring>
#include <utility>
#include <vector>

namespace model {

using base::graphic::Point;

template<class PixelType> class TD_XSides: public base::Graphic<PixelType>, public model::base::graphic::_3D  {

	public:virtual base::Graphic<PixelType>& translate(const utils::EasyMatrix<Float>& matrix) = 0;
	public:virtual void exposure(const utils::EasyMatrix<Float>& camera) = 0;
};

class CharTD_XSides : public TD_XSides<char> {

	private:typedef TD_XSides<char> Father;
	private:typedef std::pair<base::graphic::Point<Float>, base::graphic::Point<Float>> Side;
	protected:int sidesCount;
	private:char pixelChar;
	private:std::vector<Side>* sides;
	
	private:Float transi_x, transi_y, transi_z;

	private:bool disabled(void) { return sides == nullptr; }
	private:void disable(void)  {
		sides = nullptr;
	}

	public:~CharTD_XSides(void) {
		delete sides;
		disable();
	}

	public:CharTD_XSides(char showChar): sidesCount(0), pixelChar(showChar), sides(new std::vector<Side>), transi_x(0.0F), transi_y(0.0F), transi_z(0.0F) {
	}
	
	public:CharTD_XSides(const CharTD_XSides & another): sidesCount(another.sidesCount), pixelChar(another.pixelChar), sides(new std::vector<Side>(*another.sides)) {
	}
	
	public:CharTD_XSides(CharTD_XSides && another) noexcept : sidesCount(another.sidesCount), pixelChar(another.pixelChar), sides(another.sides) {
		another.disable();
	}

	public:CharTD_XSides& operator = (CharTD_XSides && another) {
		sidesCount = another.sidesCount;
		pixelChar  = another.pixelChar;
		sides      = another.sides;
		another.disable();
		return *this;
	}

	public:CharTD_XSides& operator = (const CharTD_XSides & another) {
		sidesCount = another.sidesCount;
		pixelChar  = another.pixelChar;
		sides      = new std::vector<Side>(*another.sides);
		return *this;
	}
	
	public:void addSide(Float x1, Float y1, Float z1, Float x2, Float y2, Float z2) {
		if(disabled()) return;
		sides->push_back( make_pair( Point<Float>(x1, y1, z1), Point<Float>(x2, y2, z2) ) );
	};

	protected:void trans(const utils::EasyMatrix<Float>& matrix) {
		if(disabled()) return;
		for(Side& side : *sides) {
			side.first.translate(matrix);
			side.second.translate(matrix);
		}
	}

	public:base::Graphic<char>& translate(const utils::EasyMatrix<Float>& matrix) {
		trans(matrix);
		return *this;
	}

	public:void exposure(const utils::EasyMatrix<Float>& camera) {
		if(disabled()) return;
		clear();
		for(Side side : *sides) {
			side.first.translate(camera);
			side.second.translate(camera);
			CharLine::dda(*this, side.first.x, side.first.y, side.second.x, side.second.y, pixelChar);
		}
		rebuff();
	}
	
	public:void transition(Float x, Float y, Float z) {
		transi_x += x;
		transi_y += y;
		transi_z += z;
		translate(model::base::Graphic<char>::getPositionTranslate4x4F(x, y, z));
	}
	
	public:void scale(Float x, Float y, Float z) {
		Float transi_x_old = transi_x, transi_y_old = transi_y, transi_z_old = transi_z;
		transition(-transi_x, -transi_y, -transi_z);
		translate(model::base::Graphic<char>::getScaleTranslate4x4F(x, y, z));
		transition(transi_x_old, transi_y_old, transi_z_old);
	}
	
	public:void rotate(Float x, Float y, Float z) {
		Float transi_x_old = transi_x, transi_y_old = transi_y, transi_z_old = transi_z;
		transition(-transi_x, -transi_y, -transi_z);
		translate(
			model::base::Graphic<char>::getRotationTranslate4x4F(XAxis, x) *
			model::base::Graphic<char>::getRotationTranslate4x4F(YAxis, y) *
			model::base::Graphic<char>::getRotationTranslate4x4F(ZAxis, z)
		);
		transition(transi_x_old, transi_y_old, transi_z_old);
	}
};
};

#endif
